/*
* @(#)ScreenRecorderMain.java
*
* Copyright (c) 2011 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.screenrecorder;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import org.monte.media.gui.datatransfer.DropFileTransferHandler;
import org.monte.media.gui.JLabelHyperlinkHandler;
import java.net.URISyntaxException;
import java.net.URI;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Point;
import java.io.File;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.Rectangle;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.JComponent;
import javax.swing.DefaultComboBoxModel;
import org.monte.media.Format;
import org.monte.media.gui.Worker;
import org.monte.media.math.Rational;
import java.awt.AWTException;
import java.awt.Desktop;
import java.awt.Frame;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.prefs.Preferences;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import static org.monte.media.AudioFormatKeys.*;
import static org.monte.media.VideoFormatKeys.*;
import static java.lang.Math.*;
import java.nio.ByteOrder;
import java.util.Vector;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.TargetDataLine;
/**
* ScreenRecorderMain.
*
* @author Werner Randelshofer
* @version $Id: ScreenRecorderMain.java 303 2013-01-03 07:43:37Z werner $
*/
public class ScreenRecorderMain extends javax.swing.JFrame {
private class Handler implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
ScreenRecorder r = screenRecorder;
if (r != null && r.getState() == ScreenRecorder.State.FAILED) {
recordingFailed();
}
}
}
private Handler handler = new Handler();
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd 'at' HH.mm.ss");
private volatile Worker recorder;
private ScreenRecorder screenRecorder;
private int depth;
private int format;
private int encoding;
private int cursor;
private int audioRate;
private int audioSource;
private int area;
private double screenRate;
private double mouseRate;
private File movieFolder;
private static class AudioRateItem {
private String title;
private int sampleRate;
private int bitsPerSample;
public AudioRateItem(String title, int sampleRate, int bitsPerSample) {
this.title = title;
this.sampleRate = sampleRate;
this.bitsPerSample = bitsPerSample;
}
@Override
public String toString() {
return title;
}
}
private static class AudioSourceItem {
private String title;
private Mixer.Info mixerInfo;
private boolean isEnabled;
public AudioSourceItem(String title, Mixer.Info mixerInfo) {
this(title, mixerInfo, true);
}
public AudioSourceItem(String title, Mixer.Info mixerInfo, boolean isEnabled) {
this.title = title;
this.mixerInfo = mixerInfo;
this.isEnabled = isEnabled;
}
@Override
public String toString() {
return title;
}
}
private static class AreaItem {
private String title;
/**
* Area or null for entire screen.
*/
private Dimension inputDimension;
/**
* null if same value as input dimension.
*/
private Dimension outputDimension;
/**
* SwingConstants.CENTER, .NORTH_WEST, SOUTH_WEST.
*/
private int alignment;
private Point location;
public AreaItem(String title, Dimension dim, int alignment) {
this(title, dim, null, alignment, new Point(0, 0));
}
public AreaItem(String title, Dimension inputDim, Dimension outputDim, int alignment, Point location) {
this.title = title;
this.inputDimension = inputDim;
this.outputDimension = outputDim;
this.alignment = alignment;
this.location = location;
}
@Override
public String toString() {
return title;
}
public Rectangle getBounds(GraphicsConfiguration cfg) {
Rectangle areaRect = null;
if (inputDimension != null) {
areaRect = new Rectangle(0, 0, inputDimension.width, inputDimension.height);
}
outputDimension = outputDimension;
Rectangle screenBounds = cfg.getBounds();
if (areaRect == null) {
areaRect = (Rectangle) screenBounds.clone();
}
switch (alignment) {
case SwingConstants.CENTER:
areaRect.x = screenBounds.x + (screenBounds.width - areaRect.width) / 2;
areaRect.y = screenBounds.y + (screenBounds.height - areaRect.height) / 2;
break;
case SwingConstants.NORTH_WEST:
areaRect.x = screenBounds.x;
areaRect.y = screenBounds.y;
break;
case SwingConstants.SOUTH_WEST:
areaRect.x = screenBounds.x;
areaRect.y = screenBounds.y + screenBounds.height - areaRect.height;
break;
default:
break;
}
areaRect.translate(location.x, location.y);
areaRect = areaRect.intersection(screenBounds);
return areaRect;
}
}
/**
* Creates new form ScreenRecorderMain
*/
public ScreenRecorderMain() {
((JPanel) getContentPane()).setBorder(new EmptyBorder(12, 20, 20, 20));
initComponents();
String version = ScreenRecorderMain.class.getPackage().getImplementationVersion();
if (version != null) {
int p = version.indexOf(' ');
setTitle(getTitle() + " " + version.substring(0, p == -1 ? version.length() : p));
}
final Preferences prefs = Preferences.userNodeForPackage(ScreenRecorderMain.class);
if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
movieFolder = new File(System.getProperty("user.home") + File.separator + "Videos");
} else {
movieFolder = new File(System.getProperty("user.home") + File.separator + "Movies");
}
movieFolder = new File(prefs.get("ScreenRecorder.movieFolder", movieFolder.toString()));
final String infoLabelText = infoLabel.getText();
infoLabel.setText(infoLabelText.replaceAll("\"Movies\"", "\"<a href=\"" + movieFolder.toURI() + "\">" + movieFolder.getName() + "</a>\""));
new JLabelHyperlinkHandler(infoLabel, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
File f = new File(new URI(e.getActionCommand()));
if (!f.exists()) {
f.mkdirs();
}
Desktop.getDesktop().open(f);
} catch (URISyntaxException ex) {
System.err.println("ScreenRecorderMain bad href " + e.getActionCommand() + ", " + ex);
} catch (IOException ex) {
System.err.println("ScreenRecorderMain io exception: " + ex);
}
}
});
infoLabel.setTransferHandler(new DropFileTransferHandler(JFileChooser.DIRECTORIES_ONLY, null, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
movieFolder = new File(e.getActionCommand());
prefs.put("ScreenRecorder.movieFolder", movieFolder.toString());
infoLabel.setText(infoLabelText.replaceAll("\"Movies\"", "\"<a href=\"" + movieFolder.toURI() + "\">" + movieFolder.getName() + "</a>\""));
}
}));
depth = min(max(0, prefs.getInt("ScreenRecording.colorDepth", 3)), colorsChoice.getItemCount() - 1);
colorsChoice.setSelectedIndex(depth);
format = min(max(0, prefs.getInt("ScreenRecording.format", 0)), formatChoice.getItemCount() - 1);
formatChoice.setSelectedIndex(format);
encoding = min(max(0, prefs.getInt("ScreenRecording.encoding", 0)), encodingChoice.getItemCount() - 1);
encodingChoice.setSelectedIndex(encoding);
cursor = min(max(0, prefs.getInt("ScreenRecording.cursor", 1)), cursorChoice.getItemCount() - 1);
cursorChoice.setSelectedIndex(cursor);
screenRate = prefs.getDouble("ScreenRecording.screenRate", 15);
SpinnerNumberModel screenRateModel = new SpinnerNumberModel(screenRate, 1, 30, 1);
screenRateField.setModel(screenRateModel);
mouseRate = prefs.getDouble("ScreenRecording.mouseRate", 30);
SpinnerNumberModel mouseRateModel = new SpinnerNumberModel(mouseRate, 1, 30, 1);
mouseRateField.setModel(mouseRateModel);
// FIXME - 8-bit recording is currently broken
audioRateChoice.setModel(new DefaultComboBoxModel(new Object[]{
//new AudioItem("No Audio", 0, 0),
//new AudioItem("8.000 Hz, 8-bit",8000,8),
new AudioRateItem("8.000 Hz", 8000, 16),
//new AudioItem("11.025 Hz, 8-bit",11025,8),
new AudioRateItem("11.025 Hz", 11025, 16),
//new AudioItem("22.050 Hz, 8-bit",22050,8),
new AudioRateItem("22.050 Hz", 22050, 16),
//new AudioItem("44.100 Hz, 8-bit",44100,8),
new AudioRateItem("44.100 Hz", 44100, 16),}));
audioRate = prefs.getInt("ScreenRecording.audioRate", 0);
audioRateChoice.setSelectedIndex(audioRate);
audioSourceChoice.setModel(new DefaultComboBoxModel(getAudioSources()));
audioSource = prefs.getInt("ScreenRecording.audioSource", 0);
audioSourceChoice.setSelectedIndex(audioSource);
Dimension customDim = new Dimension(prefs.getInt("ScreenRecording.customAreaWidth", 1024),
prefs.getInt("ScreenRecording.customAreaHeight", 768));
Point customLoc = new Point(
prefs.getInt("ScreenRecording.customAreaX", 100),
prefs.getInt("ScreenRecording.customAreaY", 100));
areaChoice.setModel(new DefaultComboBoxModel(new Object[]{
new AreaItem("Entire Screen", null, SwingConstants.NORTH_WEST),
new AreaItem("Center 1280 x 720", new Dimension(1280, 720), SwingConstants.CENTER),
new AreaItem("Center 1024 x 768", new Dimension(1024, 768), SwingConstants.CENTER),
new AreaItem("Center 800 x 600", new Dimension(800, 600), SwingConstants.CENTER),
new AreaItem("Center 640 x 480", new Dimension(640, 480), SwingConstants.CENTER),
new AreaItem("Top Left 1280 x 720", new Dimension(1280, 720), SwingConstants.NORTH_WEST),
new AreaItem("Top Left 1024 x 768", new Dimension(1024, 768), SwingConstants.NORTH_WEST),
new AreaItem("Top Left 800 x 600", new Dimension(800, 600), SwingConstants.NORTH_WEST),
new AreaItem("Top Left 640 x 480", new Dimension(640, 480), SwingConstants.NORTH_WEST),
new AreaItem("Bottom Left 1280 x 720", new Dimension(1280, 720), SwingConstants.SOUTH_WEST),
new AreaItem("Bottom Left 1024 x 768", new Dimension(1024, 768), SwingConstants.SOUTH_WEST),
new AreaItem("Bottom Left 800 x 600", new Dimension(800, 600), SwingConstants.SOUTH_WEST),
new AreaItem("Bottom Left 640 x 480", new Dimension(640, 480), SwingConstants.SOUTH_WEST),
new AreaItem("Custom: " + customLoc.x + ", " + customLoc.y + "; " + customDim.width + " x " + customDim.height + "", customDim, null, SwingConstants.NORTH_WEST, customLoc),}));
areaChoice.setMaximumRowCount(16);
area = prefs.getInt("ScreenRecording.area", 0);
areaChoice.setSelectedIndex(min(areaChoice.getItemCount() - 1, max(0, area)));
getRootPane().setDefaultButton(startStopButton);
updateEncodingChoice();
pack();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
formatLabel = new javax.swing.JLabel();
formatChoice = new javax.swing.JComboBox();
colorsLabel = new javax.swing.JLabel();
colorsChoice = new javax.swing.JComboBox();
infoLabel = new javax.swing.JLabel();
startStopButton = new javax.swing.JButton();
mouseLabel = new javax.swing.JLabel();
cursorChoice = new javax.swing.JComboBox();
audioRateLabel = new javax.swing.JLabel();
audioRateChoice = new javax.swing.JComboBox();
screenRateLabel = new javax.swing.JLabel();
screenRateField = new javax.swing.JSpinner();
mouseRateLabel = new javax.swing.JLabel();
mouseRateField = new javax.swing.JSpinner();
encodingLabel = new javax.swing.JLabel();
encodingChoice = new javax.swing.JComboBox();
areaLabel = new javax.swing.JLabel();
areaChoice = new javax.swing.JComboBox();
selectAreaButton = new javax.swing.JButton();
stateLabel = new javax.swing.JLabel();
audioSourceLabel = new javax.swing.JLabel();
audioSourceChoice = new javax.swing.JComboBox();
FormListener formListener = new FormListener();
setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
setTitle("Monte Screen Recorder");
setResizable(false);
addWindowListener(formListener);
formatLabel.setText("Format:");
formatChoice.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "AVI", "QuickTime" }));
formatChoice.addActionListener(formListener);
colorsLabel.setText("Colors:");
colorsChoice.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Hundreds", "Thousands", "Millions" }));
infoLabel.setFont(new java.awt.Font("Lucida Grande", 0, 11)); // NOI18N
infoLabel.setText("<html>The recording will be stored in the folder \"Movies\".<br> Drop a folder on this text to change the storage location.<br> A new file will be created every hour or when the file size limit is reached.<br> <br>This window will be minized before the recording starts.<br> To stop the recording, restore this window and press the Stop button.<br> ");
startStopButton.setText("Start");
startStopButton.addActionListener(formListener);
mouseLabel.setText("Mouse:");
cursorChoice.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "No Cursor", "Black Cursor", "White Cursor" }));
audioRateLabel.setText("Audio Rate:");
audioRateChoice.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "44.100 kHz" }));
screenRateLabel.setText("Screen Rate:");
mouseRateLabel.setText("Mouse Rate:");
encodingLabel.setText("Encoding:");
encodingChoice.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Screen Capture", "Run Length", "None", "PNG", "JPEG 100 %", "JPEG 50 %" }));
areaLabel.setText("Area:");
areaChoice.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Entire Screen", "0 0, 1024 x 768", " " }));
selectAreaButton.setText("Custom Area...");
selectAreaButton.addActionListener(formListener);
stateLabel.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
stateLabel.setText(" ");
audioSourceLabel.setText("Audio:");
audioSourceChoice.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "No Audio", "44.100 kHz" }));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(stateLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 190, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(startStopButton)
.addGap(10, 10, 10))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(colorsLabel)
.addComponent(mouseLabel)
.addComponent(formatLabel)
.addComponent(areaLabel)
.addComponent(audioSourceLabel))
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(formatChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(colorsChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(cursorChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(audioSourceChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(screenRateLabel, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(mouseRateLabel, javax.swing.GroupLayout.Alignment.TRAILING))
.addComponent(encodingLabel)
.addComponent(audioRateLabel))
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(audioRateChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(encodingChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(screenRateField, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(mouseRateField, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGroup(layout.createSequentialGroup()
.addComponent(areaChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(selectAreaButton))))
.addComponent(infoLabel))
.addContainerGap())
);
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {audioRateChoice, colorsChoice, cursorChoice, formatChoice});
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(formatChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(formatLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(colorsChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(colorsLabel))
.addGap(11, 11, 11)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cursorChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(mouseLabel)))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(encodingChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(encodingLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(screenRateField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(screenRateLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(mouseRateField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(mouseRateLabel))))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(audioRateChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(audioRateLabel)
.addComponent(audioSourceLabel)
.addComponent(audioSourceChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(areaChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(areaLabel)
.addComponent(selectAreaButton))
.addGap(18, 18, 18)
.addComponent(infoLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 8, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(startStopButton)
.addComponent(stateLabel))
.addContainerGap())
);
pack();
}
// Code for dispatching events from components to event handlers.
private class FormListener implements java.awt.event.ActionListener, java.awt.event.WindowListener {
FormListener() {}
public void actionPerformed(java.awt.event.ActionEvent evt) {
if (evt.getSource() == formatChoice) {
ScreenRecorderMain.this.formatChoicePerformed(evt);
}
else if (evt.getSource() == startStopButton) {
ScreenRecorderMain.this.startStopPerformed(evt);
}
else if (evt.getSource() == selectAreaButton) {
ScreenRecorderMain.this.selectAreaPerformed(evt);
}
}
public void windowActivated(java.awt.event.WindowEvent evt) {
}
public void windowClosed(java.awt.event.WindowEvent evt) {
}
public void windowClosing(java.awt.event.WindowEvent evt) {
if (evt.getSource() == ScreenRecorderMain.this) {
ScreenRecorderMain.this.formWindowClosing(evt);
}
}
public void windowDeactivated(java.awt.event.WindowEvent evt) {
}
public void windowDeiconified(java.awt.event.WindowEvent evt) {
if (evt.getSource() == ScreenRecorderMain.this) {
ScreenRecorderMain.this.formWindowDeiconified(evt);
}
}
public void windowIconified(java.awt.event.WindowEvent evt) {
}
public void windowOpened(java.awt.event.WindowEvent evt) {
}
}// </editor-fold>//GEN-END:initComponents
private static Vector<AudioSourceItem> getAudioSources() {
Vector<AudioSourceItem> l = new Vector<AudioSourceItem>();
l.add(new AudioSourceItem("None", null, false));
l.add(new AudioSourceItem("Default Input", null, true));
Mixer.Info[] mixers = AudioSystem.getMixerInfo();
DataLine.Info lineInfo = new DataLine.Info(
TargetDataLine.class,
new javax.sound.sampled.AudioFormat(
44100.0f,
16,
2,
true,
true));
for (Mixer.Info info : mixers) {
Mixer mixer = AudioSystem.getMixer(info);
if (mixer.isLineSupported(lineInfo)) {
l.add(new AudioSourceItem(info.getName(), info));
}
}
return l;
}
private void updateValues() {
Preferences prefs = Preferences.userNodeForPackage(ScreenRecorderMain.class);
format = formatChoice.getSelectedIndex();
prefs.putInt("ScreenRecording.format", format);
encoding = encodingChoice.getSelectedIndex();
prefs.putInt("ScreenRecording.encoding", encoding);
depth = colorsChoice.getSelectedIndex();
prefs.putInt("ScreenRecording.colorDepth", depth);
cursor = cursorChoice.getSelectedIndex();
prefs.putInt("ScreenRecording.cursor", cursor);
audioRate = audioRateChoice.getSelectedIndex();
prefs.putInt("ScreenRecording.audioRate", audioRate);
audioSource = audioSourceChoice.getSelectedIndex();
prefs.putInt("ScreenRecording.audioSource", audioSource);
area = areaChoice.getSelectedIndex();
prefs.putInt("ScreenRecording.area", area);
if (screenRateField.getValue() instanceof Double) {
screenRate = (Double) screenRateField.getValue();
prefs.putDouble("ScreenRecording.screenRate", screenRate);
}
if (mouseRateField.getValue() instanceof Double) {
mouseRate = (Double) mouseRateField.getValue();
prefs.putDouble("ScreenRecording.mouseRate", mouseRate);
}
}
private void start() throws IOException, AWTException {
updateValues();
if (screenRecorder == null) {
setSettingsEnabled(false);
stateLabel.setForeground(Color.RED);
stateLabel.setText("Recording...");
String mimeType;
String videoFormatName, compressorName;
float quality = 1.0f;
int bitDepth;
switch (depth) {
default:
case 0:
bitDepth = 8;
break;
case 1:
bitDepth = 16;
break;
case 2:
bitDepth = 24;
break;
}
switch (format) {
default:
case 0:
mimeType = MIME_AVI;
switch (encoding) {
case 0:
default:
videoFormatName = compressorName = ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE;
break;
case 1:
videoFormatName = compressorName = ENCODING_AVI_RLE;
bitDepth = 8;
break;
case 2:
videoFormatName = compressorName = ENCODING_AVI_DIB;
if (bitDepth == 16) {
bitDepth = 24;
}
break;
case 3:
videoFormatName = compressorName = ENCODING_AVI_PNG;
bitDepth = 24;
break;
case 4:
videoFormatName = compressorName = ENCODING_AVI_MJPG;
bitDepth = 24;
break;
case 5:
videoFormatName = compressorName = ENCODING_AVI_MJPG;
bitDepth = 24;
quality = 0.5f;
break;
}
break;
case 1:
mimeType = MIME_QUICKTIME;
switch (encoding) {
case 0:
default:
if (bitDepth == 8) {
// FIXME - 8-bit Techsmith Screen Capture is broken
videoFormatName = ENCODING_QUICKTIME_ANIMATION;
compressorName = COMPRESSOR_NAME_QUICKTIME_ANIMATION;
} else {
videoFormatName = compressorName = ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE;
compressorName = ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE;
}
break;
case 1:
videoFormatName = ENCODING_QUICKTIME_ANIMATION;
compressorName = COMPRESSOR_NAME_QUICKTIME_ANIMATION;
break;
case 2:
videoFormatName = ENCODING_QUICKTIME_RAW;
compressorName = COMPRESSOR_NAME_QUICKTIME_RAW;
break;
case 3:
videoFormatName = ENCODING_QUICKTIME_PNG;
compressorName = COMPRESSOR_NAME_QUICKTIME_PNG;
bitDepth = 24;
break;
case 4:
videoFormatName = ENCODING_QUICKTIME_JPEG;
compressorName = COMPRESSOR_NAME_QUICKTIME_JPEG;
bitDepth = 24;
break;
case 5:
videoFormatName = ENCODING_QUICKTIME_JPEG;
compressorName = COMPRESSOR_NAME_QUICKTIME_JPEG;
bitDepth = 24;
quality = 0.5f;
break;
}
break;
}
Mixer.Info mixerInfo;
int audioRate;
int audioBitsPerSample;
{
AudioSourceItem src = (AudioSourceItem) audioSourceChoice.getItemAt(this.audioSource);
AudioRateItem rate = (AudioRateItem) audioRateChoice.getItemAt(this.audioRate);
mixerInfo = src.mixerInfo;
audioRate = src.isEnabled ? rate.sampleRate : 0;
audioBitsPerSample = rate.bitsPerSample;
}
String crsr;
switch (cursor) {
default:
case 0:
crsr = null;
break;
case 1:
crsr = ScreenRecorder.ENCODING_BLACK_CURSOR;
break;
case 2:
crsr = ScreenRecorder.ENCODING_WHITE_CURSOR;
break;
}
GraphicsConfiguration cfg = getGraphicsConfiguration();
Rectangle areaRect = null;
Dimension outputDimension = null;
AreaItem item = (AreaItem) areaChoice.getItemAt(area);
areaRect = item.getBounds(cfg);
outputDimension = item.outputDimension;
if (outputDimension == null) {
outputDimension = areaRect.getSize();
}
screenRecorder = new ScreenRecorder(cfg, areaRect,
// the file format:
new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, mimeType),
//
// the output format for screen capture:
new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey, videoFormatName,
CompressorNameKey, compressorName,
WidthKey, outputDimension.width,
HeightKey, outputDimension.height,
DepthKey, bitDepth, FrameRateKey, Rational.valueOf(screenRate),
QualityKey, quality,
KeyFrameIntervalKey, (int) (screenRate * 60) // one keyframe per minute is enough
),
//
// the output format for mouse capture:
crsr == null ? null : new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey, crsr,
FrameRateKey, Rational.valueOf(mouseRate)),
//
// the output format for audio capture:
audioRate == 0 ? null : new Format(MediaTypeKey, MediaType.AUDIO,
//EncodingKey, audioFormatName,
SampleRateKey, Rational.valueOf(audioRate),
SampleSizeInBitsKey, audioBitsPerSample),
//
// the storage location of the movie
movieFolder);
if (mixerInfo != null) {
screenRecorder.setAudioMixer(AudioSystem.getMixer(mixerInfo));
}
startStopButton.setText("Stop");
screenRecorder.addChangeListener(handler);
screenRecorder.start();
}
}
public void setSettingsEnabled(boolean b) {
for (Component c : getContentPane().getComponents()) {
if (c != startStopButton && c != stateLabel) {
c.setEnabled(b);
}
}
((JComponent) getContentPane()).invalidate();
((JComponent) getContentPane()).revalidate();
}
private void stop() {
if (screenRecorder != null) {
final ScreenRecorder r = screenRecorder;
startStopButton.setEnabled(false);
stateLabel.setForeground(Color.RED);
stateLabel.setText("Stopping...");
screenRecorder = null;
new Worker() {
@Override
protected Object construct() throws Exception {
r.stop();
return null;
}
@Override
protected void finished() {
ScreenRecorder.State state = r.getState();
setSettingsEnabled(true);
startStopButton.setEnabled(true);
startStopButton.setText("Start");
stateLabel.setForeground(Color.RED);
stateLabel.setText(" ");
}
}.start();
}
}
private void recordingFailed() {
if (screenRecorder != null) {
screenRecorder = null;
startStopButton.setEnabled(true);
startStopButton.setText("Start");
setExtendedState(Frame.NORMAL);
JOptionPane.showMessageDialog(ScreenRecorderMain.this,
"<html><b>Sorry. Screen Recording failed.</b>",
"Screen Recorder", JOptionPane.ERROR_MESSAGE);
}
}
private void updateEncodingChoice() {
int index = encodingChoice.getSelectedIndex();
switch (formatChoice.getSelectedIndex()) {
case 0: // AVI
encodingChoice.setModel(
new javax.swing.DefaultComboBoxModel(new String[]{"Screen Capture", "Run Length", "None", "PNG", "JPEG 100 %", "JPEG 50 %"}));
break;
case 1: // QuickTime
encodingChoice.setModel(
new javax.swing.DefaultComboBoxModel(new String[]{"Screen Capture", "Animation", "None", "PNG", "JPEG 100 %", "JPEG 50 %"}));
break;
}
encodingChoice.setSelectedIndex(index);
}
private void startStopPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startStopPerformed
if (screenRecorder == null) {
setExtendedState(Frame.ICONIFIED);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
start();
} catch (Throwable t) {
t.printStackTrace();
setExtendedState(Frame.NORMAL);
JOptionPane.showMessageDialog(ScreenRecorderMain.this,
"<html><b>Sorry. Screen Recording failed.</b><br>" + t.getMessage(),
"Screen Recorder", JOptionPane.ERROR_MESSAGE);
stop();
}
}
});
} else {
stop();
}
}//GEN-LAST:event_startStopPerformed
private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
stop();
setVisible(false);
dispose();
}//GEN-LAST:event_formWindowClosing
private void formWindowDeiconified(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowDeiconified
// stop();
}//GEN-LAST:event_formWindowDeiconified
private void formatChoicePerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_formatChoicePerformed
updateEncodingChoice();
}//GEN-LAST:event_formatChoicePerformed
private void selectAreaPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAreaPerformed
final JRecordingAreaFrame f = new JRecordingAreaFrame();
AreaItem ai = (AreaItem) areaChoice.getSelectedItem();
Rectangle r = ai.getBounds(getGraphicsConfiguration());
f.setBounds(r);
f.updateLabel();
f.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
areaChoice.setSelectedIndex(areaChoice.getItemCount() - 1);
AreaItem ai = (AreaItem) areaChoice.getSelectedItem();
ai.location = f.getLocation();
ai.inputDimension = f.getSize();
ai.alignment = SwingConstants.NORTH_WEST;
ai.outputDimension = null;
ai.title = "Custom: " + ai.location.x + ", " + ai.location.y + "; " + ai.inputDimension.width + " x " + ai.inputDimension.height;
f.setVisible(false);
f.dispose();
setVisible(true);
f.removeWindowListener(this);
final Preferences prefs = Preferences.userNodeForPackage(ScreenRecorderMain.class);
prefs.putInt("ScreenRecording.customAreaX", ai.location.x);
prefs.putInt("ScreenRecording.customAreaY", ai.location.y);
prefs.putInt("ScreenRecording.customAreaWidth", ai.inputDimension.width);
prefs.putInt("ScreenRecording.customAreaHeight", ai.inputDimension.height);
((JComponent) getContentPane()).invalidate();
((JComponent) getContentPane()).revalidate();
}
});
setVisible(false);
f.setVisible(true);
}//GEN-LAST:event_selectAreaPerformed
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
//ignore
}
new ScreenRecorderMain().setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JComboBox areaChoice;
private javax.swing.JLabel areaLabel;
private javax.swing.JComboBox audioRateChoice;
private javax.swing.JLabel audioRateLabel;
private javax.swing.JComboBox audioSourceChoice;
private javax.swing.JLabel audioSourceLabel;
private javax.swing.JComboBox colorsChoice;
private javax.swing.JLabel colorsLabel;
private javax.swing.JComboBox cursorChoice;
private javax.swing.JComboBox encodingChoice;
private javax.swing.JLabel encodingLabel;
private javax.swing.JComboBox formatChoice;
private javax.swing.JLabel formatLabel;
private javax.swing.JLabel infoLabel;
private javax.swing.JLabel mouseLabel;
private javax.swing.JSpinner mouseRateField;
private javax.swing.JLabel mouseRateLabel;
private javax.swing.JSpinner screenRateField;
private javax.swing.JLabel screenRateLabel;
private javax.swing.JButton selectAreaButton;
private javax.swing.JButton startStopButton;
private javax.swing.JLabel stateLabel;
// End of variables declaration//GEN-END:variables
}